#include "at_base.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

/**
 * @brief 寻找子数据串
 * @param pdata 数据指针
 * @param size 数据长度
 * @param subdata 子数据指针
 * @param subdatasize 子串长度 
 * @retval uint8_t* 
 */
uint8_t *ATBase::data_find_sub(const uint8_t *pdata,
                               size_t size,
                               const uint8_t *subdata,
                               uint8_t subdatasize)
{
    int i, j = 0;
    // 搜索子数据串 "abcdeefght" "de"
    for (i = 0; i < size; i++)
    {
        if (pdata[i] == subdata[j])
        {
            j++;
            if (j == subdatasize)
            {
                return (uint8_t*)(pdata + i - subdatasize + 1);
            }
        }
        else
        {
            if (size - i < subdatasize) break;
            i -= j;
            j = 0;
        }
    }
	return NULL;
}

ATBase::ATBase(void)
{
    this->arg = NULL;
    this->errors = 0;
    this->pCurCmd = NULL;
    this->pTailCmd = NULL;
    this->pIdleCmd = NULL;
    this->cmdCnt = 0;
    this->idleCmdCnt = 0;
}


int ATBase::tick(uint32_t timeMs)
{
    // 当前没有命令
    if (this->pCurCmd == NULL)
    {
        return 0;
    }
    if (this->pCurCmd->__state == this->CMD_STATE_WAIT)
    {
        // 发送命令字符
        this->cmd_send((uint8_t*)&this->pCurCmd->cmd, this->pCurCmd->cmd_size);
        this->pCurCmd->__state = this->CMD_STATE_SEND;
        this->rxSize = 0; // 重置接收计数
        this->time = 0;   // 重置计时
        return 0;
    }
    if (this->pCurCmd->__state != this->CMD_STATE_SEND)
    {
        return 0;
    }
    this->time += timeMs;
    // 检查是否超时
    if (this->time >= this->pCurCmd->timeout)
    {
        if (this->pCurCmd->event_handle_fn != NULL)
            this->pCurCmd->event_handle_fn(this->arg, this->pCurCmd, EVENT_TIMEOUT);
        if (this->retry > 0)
        {
            if (this->retry != this->CMD_RETRY_INFINITE)
            {
                this->retry--;
            }
            // 重新发送命令
            this->pCurCmd->__state = this->CMD_STATE_WAIT;
            return 0;
        }
        // 超时且重试完成，产生命令失败事件
        this->pCurCmd->__state = this->CMD_STATE_FAILED;
        if (this->pCurCmd->event_handle_fn != NULL)
        {
            this->pCurCmd->event_handle_fn(this->arg, this->pCurCmd, EVENT_FAILED);
        }
    }
    return -1;
}


int ATBase::receive_callback(const uint8_t *rxdata, uint16_t size)
{
    if (rxdata == NULL || size == 0)
    {
        return 0;
    }
    if (this->pCurCmd == NULL || this->pCurCmd->__state != this->CMD_STATE_SEND)
    {
        // 当前无命令执行
        return 0;
    }
    at_cmd_t *pcmd = this->pCurCmd;
    // 判断接收字符数是否到达预期
    if (this->rxSize < pcmd->expect_size)
    {
        // 如果接收缓冲区已满，则丢弃多余数据
        if ((this->rxSize + size) > this->RX_BUFF_MAX_LENGTH)
        {
            this->errors |= this->ERROR_BUFFER_OVERFLOW;
            memcpy(this->rxData + this->rxSize, rxdata, this->RX_BUFF_MAX_LENGTH - this->rxSize);
            this->rxSize = this->RX_BUFF_MAX_LENGTH;
            if (pcmd->event_handle_fn != NULL)
                pcmd->event_handle_fn(this->arg, this->pCurCmd, EVENT_FULLED);
        }
        else
        {
            memcpy(this->rxData + this->rxSize, rxdata, size);
            this->rxSize += size;
            // 接收未完成
            if (this->rxSize < pcmd->expect_size)
                return 1;
        }
    }
    // 已经达到预期长度，则判断是否匹配预期
    if (pcmd->reply != NULL)
    {
        uint8_t *pSub = NULL;
        pSub = data_find_sub((uint8_t *)this->rxData,
                             this->rxSize,
                             (uint8_t *)pcmd->reply,
                             strlen(pcmd->reply));
        if (pSub != NULL)
        {
            // 匹配成功,触发事件
            if (pcmd->event_handle_fn != NULL)
                pcmd->event_handle_fn(this->arg, this->pCurCmd, EVENT_MATCH);
            // 切换到下一条命令
            this->next();
            return 0;
        }
        // 不匹配，则清空接收缓冲区，重新接收
        this->rxSize = 0;
        return 2;
    }
    // 注册了回调，用户自行判断接收数据
    if (pcmd->event_handle_fn != NULL)
    {
        pcmd->event_handle_fn(this->arg, this->pCurCmd, EVENT_RECV);
    }
	return 0;
    
}

int ATBase::receive_callback(const char *rxdata)
{
    if (rxdata == NULL)
    {
        return 0;
    }
    return this->receive_callback((const uint8_t *)rxdata, strlen(rxdata));
}

bool ATBase::push(uint16_t pos, at_cmd_t *pCmd, ...)
{
    AT_BASE_ASSERT(pCmd != NULL);
    AT_BASE_ASSERT(pCmd->cmd_size > 0);
    AT_BASE_ASSERT(pCmd->cmd_size < this->CMD_STRING_MAX_LENGTH);
    AT_BASE_ASSERT(pCmd->expect_size < this->RX_BUFF_MAX_LENGTH);
    /* string format start */
    va_list args;
    va_start(args, pCmd);
    size_t cnt = vsnprintf(this->formatBuffer,
                           this->CMD_STRING_MAX_LENGTH,
                           pCmd->cmd, args);
    va_end(args);
    /* string format end */
    memcpy(pCmd->cmd, this->formatBuffer, cnt);
    pCmd->cmd_size = cnt;
    pCmd->__next = NULL;
    pCmd->__state = this->CMD_STATE_WAIT;
    // 将命令写入链表，在tick中异步处理
    this->insert(pCmd, pos);
    return true;
}

/**
 * @brief 添加一个命令到队列
 * @param pCmd 命令参数结构体
 * @param ... 命令的可变参数
 * @retval true 
 * @retval false 
 */
bool ATBase::push(at_cmd_t *pCmd, ...)
{
    AT_BASE_ASSERT(pCmd != NULL);
    AT_BASE_ASSERT(pCmd->cmd_size > 0);
    AT_BASE_ASSERT(pCmd->cmd_size + 1 < this->CMD_STRING_MAX_LENGTH);
    AT_BASE_ASSERT(pCmd->expect_size < this->RX_BUFF_MAX_LENGTH);
    /* string format start */
    va_list args;
    va_start(args, pCmd);
    pCmd->cmd[pCmd->cmd_size] = '\0';
    size_t cnt = vsnprintf(this->formatBuffer,
                           this->CMD_STRING_MAX_LENGTH,
                           pCmd->cmd,
                           args);
    va_end(args);
    /* string format end */
    memcpy(pCmd->cmd, this->formatBuffer, cnt);
    pCmd->cmd_size = cnt;
    pCmd->__next = NULL;
    pCmd->__state = this->CMD_STATE_WAIT;
    // 将命令写入链表，在tick中异步处理
    this->insert(pCmd, this->cmdCnt);
    return true;
}


/**
 * @brief 切换到下一个命令
 * @retval int 0 成功
 */
int ATBase::next(void)
{
    at_cmd_t *cur;
    // 读取命令信息
    if (this->pCurCmd == NULL)
    {
        return 1;
    }
    cur = this->pCurCmd;
    this->remove(0); // 切换到下一个命令
    // 将当前命令放到空闲链表中
    this->insert_idle_list(cur);
    // 用户注册了回调，触发事件
    if (cur->event_handle_fn != NULL)
    {
        // note: 可以在该事件中回收内存
        cur->event_handle_fn(this->arg, cur, EVENT_CMDEND);
    }
    if (this->pCurCmd != NULL)
    {
        this->retry = this->pCurCmd->retry;
    }
    return 0;
}

/**
 * @brief 插入一个命令到指定位置的后面
 * @param cmd 命令结构体
 * @param pos 插入位置 (0表示插入在当前命令的后面)
 * @retval true 成功
 * @retval false 失败
 */
bool ATBase::insert(at_cmd_t *cmd, uint16_t pos)
{
    at_cmd_t *pcmd = this->pCurCmd;
    if (pos >= this->cmdCnt) // 超出范围放到最后一个
    {
        if (this->cmdCnt == 0)
        {
            // 链表为空，直接添加
            this->pCurCmd = cmd;
            this->pTailCmd = cmd;
            this->retry = cmd->retry; // 设置重试次数
        }
        else
        {
            // 链表不为空，添加到尾部
            this->pTailCmd->__next = cmd;
            this->pTailCmd = cmd;
        }
        this->cmdCnt++;
        return true;
    }
    for (size_t i = 0; i < pos; i++)
    {
        AT_BASE_ASSERT(pcmd != NULL);
        pcmd = pcmd->__next;
    }
    cmd->__next = pcmd->__next; // 插入到指定pos的后面
    pcmd->__next = cmd;
    this->cmdCnt++;
    return true;
}

ATBase::at_cmd_t * ATBase::remove(uint16_t pos)
{
    at_cmd_t *pCmd = this->pCurCmd;
    at_cmd_t *pCmdRemove = NULL;

    if (pos >= this->cmdCnt)
    {
        return NULL;
    }
    if (pos == 0)
    {
        // 删除第一个
        pCmdRemove = this->pCurCmd;
        this->pCurCmd = pCmdRemove->__next;
        goto label_remove;
    }
    for (size_t i = 0; i < pos - 1; i++)
    {
        pCmd = pCmd->__next;
    }
    if (pos == this->cmdCnt - 1)
    {
        // 删除最后一个
        this->pTailCmd = pCmd;
        pCmdRemove = pCmd->__next;
        goto label_remove;
    }
    else
    {
        pCmdRemove = pCmd->__next;
        pCmd->__next = pCmdRemove->__next;
        goto label_remove;
    }

    label_remove:
    {
        pCmdRemove->__next = NULL;
        pCmdRemove->__state = this->CMD_STATE_IDLE;
        this->cmdCnt--;
        return pCmdRemove;
    }
}

void ATBase::insert_idle_list(at_cmd_t *cmd)
{
    if (this->pIdleCmd == NULL)
    {
        // 没有空闲命令，则将该命令置为空闲
        cmd->__next = NULL;
        this->pIdleCmd = cmd;
    }
    else
    {
        // 有空闲命令，则将该命令添加到空闲链表头部
        cmd->__next = this->pIdleCmd;
        this->pIdleCmd = cmd;
    }
    this->idleCmdCnt++; // 空闲命令数量加一
}

/**
 * @brief 清空所有命令
 */
void ATBase::clear(void)
{
    while(this->pCurCmd != NULL)
    {
        this->insert_idle_list(this->remove(0));
    }
}

/**
 * @brief 清除指定id的命令
 * @param id 命令ID
 * @retval true 
 * @retval false 
 */
bool ATBase::clear(uint8_t id)
{
    at_cmd_t *pcmd = this->pCurCmd;
    uint16_t pos = 0;
    while (pcmd != NULL)
    {
        if (pcmd->id == id)
        {
            if (pos == 0)
            {
                // 当前命令正在执行，无法切换
                return false;
            }
            else
            {
                remove(pos);
                insert_idle_list(pcmd);
                pcmd = pcmd->__next;
                continue;
            }
        }
        pos++;
        pcmd = pcmd->__next;
    }
    return false;
}

/**
 * @brief 获取当前执行命令的id
 * @retval int >0 有效
 */
int ATBase::get_cmd_id(void)
{
    if (this->pCurCmd == NULL)
        return -1;
    return this->pCurCmd->id;
}

/**
 * @brief 获取当前队列中的命令数量
 * @retval int 
 */
int ATBase::get_cmd_num(void)
{
    return this->cmdCnt;
}

int ATBase::get_idle_cmd_num(void)
{
    return this->idleCmdCnt;
}

/**
 * @brief 获取当前命令
 * @retval ATBase::at_cmd_t* 
 */
ATBase::at_cmd_t * ATBase::get_cmd(void)
{
    return this->pCurCmd;
}

ATBase::at_cmd_t * ATBase::push_idle_cmd(at_cmd_t *cmd)
{
    AT_BASE_ASSERT(cmd != NULL);
    insert_idle_list(cmd);
    return 0;
}

ATBase::at_cmd_t * ATBase::pop_idle_cmd(void)
{
    if (this->pIdleCmd == NULL)
        return NULL;
    at_cmd_t *cmd = this->pIdleCmd;
    this->pIdleCmd = this->pIdleCmd->__next;
    cmd->__next = NULL;
    this->idleCmdCnt--;
    return cmd;
}
